home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The X-Philes (2nd Revision)
/
The X-Philes Number 1 (1995).iso
/
xphiles
/
hp48hor1
/
dots.doc
< prev
next >
Wrap
Text File
|
1995-03-31
|
18KB
|
402 lines
(Comp.sys.handhelds)
Item: 3834 by catto at author.ecn.purdue.edu
Author: [Erin S Catto]
Subj: Fuzz & ML Blurbs
Date: Tue Aug 13 1991
Well I promised myself that I would pass on what I've learned once I figured
out what's going on with this machine language business. Sorry, it's not a
game. Anyway, here's a small program which simply draws random dots on the
stack display and exits when you press the drop key.
I thank Jan Brittenson for the STAR assembler (excellent) and the MLDL (way
excellent).
This program covers most of the key hardware questions a freshie might have.
+ Drawing on the stack display at a coordinate (0-127,0-63)
+ Data storage acquisition so that data tables are temporary
+ Turning off keyboard interupts (go ahead, press ON-A-F, nothing happens)
+ Machine code keyboard scanning
+ How to get random numbers you can't depend on
I'll briefly cover a few topics:
Preserving the RPL Operating System
===================================
The HP48 has an operating system just like any other computer. It was written
in System RPL and machine code. The HP48 OS uses the registers d0, d1, b, and
d to take care of business, such as memory management and RPL command pointing.
Thus you must save these important register and restore them at the termination
of your machine code program. You must also set the OS back on track. Here's
how it's done:
save_regs = #0679B ;ml routines to handle these tasks
rest_regs = #067D2
begin
call.a save_regs ;save registers
.
.
.
call.a rest_regs ;restore registers
move.a @d0, a ;a = next RPL instruction
add.a 5, d0 ;point d0 to following instruction
jump @a ;jump to instruction in a
;address of object prolog?
end
Here's how I believe the last three instructions work:
d0
|
v
RPL instruction stream: ... 11EC7|65547|EF9D7|83188|...
move.a @d0, a ;a gets 74556
add.a 5, d0 ;does the following
d0
|
v
RPL instruction stream: ... 11EC7|65547|EF9D7|83188|...
Lets say at the address 74556 there is the RPL program prolog 02D9D then
jump @a
will jump to the address 02D9D, a chain of pointers.
Now the operating system is back on track, refreshing the screen, handling the
keyboard, etc. Evidence of this is seen when you exit the attached program,
the RPL OS automatically returns the screen to its normal state.
Drawing on the Stack Display
============================
The stack display contains two grobs: the stack-message grob and the menu grob.
Statistics:
+ Screen Grob: location of grob contained at #7055B
size = 56 rows high by 136 bits wide (34 nibbles)
+ Menu Grob: location of grob contained at #70551
size = 8 rows high by 136 bits wide
Once you get to the grob in memory you must skip the 20 nibble prolog to get to
the pixel data. The pixel data begins at the upper left corner of the display
and goes left to right, wrapping to the next row after 34 nibbles. While the
nibbles go left to right as the address increases the bits in each nibble are
in right to left order.
Example: On Screen: 0101
In memory: 1010 = #A
It is useful to have a data table which contains the address of the start of
each row of pixels. This way you can take an arbitrary y coordinate and
transform it into an index to the data simply by multiplying y by 5. Lets
look at this:
Say y = 4, then 5 * y = 20, now the data table looks like this:
start y=0 y=1 y=2 y=3 y=4
| | | | |
v v v v v
04347|26347|48347|6A347|8C347..... (stream of row addresses)
So you use start+20 to get the row address. Below I talk about building a
data
string.
Here's my plug for using the stack display instead of PICT.
1) Let PICT be the user's territory.
2) PICT sits and takes up 1098 bytes, unless you PURGE it.
3) The stack display is always the same size (131x64)
Making Data Space
=================
Quite often data tables are used in mcode programs. If the data is going to be
generated by the program then it becomes a waste of space to attach non-
executing data to your program. A routine at #05B7D will make a string object
whose data length (not including the header) is specified by the contents of
the c reqister. The address is returned in the address register d0.
d0
v
C2A20|04000|123DF8734872394729834... ...2312|
<- header->|<--- length = c -----... ...--->|
All you need to do is:
move.p5 c, length ;you specify length
call make_str ;make_str = #05B7D
swap.a a, d0 ;d0 = string data area address
move.w a, r0 ;store the address somewhere for reference
Turning Off Interupts
=====================
Turning off interupts means turning off the keyboard, in a manner of speaking.
It means your program will only be interupted when you want input. It is
simple to turn the interupts off, but you must be sure to turn them back on.
Interupts Off: clrb 15, st ;clear bit 15 of status
register
intoff
Interupts On: inton
setb 15, st ;set bit 15 of status register
It would be nice if someone in-the-know would explain the flags the ST register
contains.
Keyboard input, the ML way
===========================
The instructions which must be used to grab key presses are OUT and IN.
Sounds simple enough, sure if you know how to do it. Here's how it's done
1) load register c with the row of keys you want to check (see table below)
2) perform OUT.X C to tell your HP to check that row
3) call IN_C = #01160, this address only contains: IN.4 C
RET
It seems that IN.4 C must be called, I think it messes with the
return stack.
4) move the adress field of register c somewhere safe, say register a
5) clear register c
6) perform OUT.X C to tell your HP to stop checking
7) now if you stored register c in register a in step (4) then the low
nibbles of reg. a will contain bits set corresponding to the keys pressed
in the row of interest.
The Keyboard Map: (from an article by Jan Brittenson)
(IN) #20 #10 #08 #04 #02 #01
---------------------------
(OUT) | 6 5 4 3 2 1
#100 | B C D E F
#080 | PRG CST VAR ^ NXT
#040 | STO EVL < v >
#020 | COS TAN sqt pwr inv
#010 | ON* ENT +/- EEX DEL <==
#008 | alp SIN 7 8 9 /
#004 | yel MTH 4 5 6 x
#002 | blu A 1 2 3 -
#001 | ' 0 . SPC +
(*) The ON key is actually in a column of its own.
#xxx refers to the keyboard scan bits. The column
scan bit for the ON key is #8000.
The numbers in the leftmost column are use to specify the row in the OUT
command and the numbers in the topmost row correspond to a positive result
from the IN command. Note that these numbers have a binary representation
which has only one bit set. So multiple bits set by the IN command mean
multiple key presses.
The Random Thing
================
Your HP48sx maintains a hardware checksum at #00104 which is constantly
changing in a seemingly unpredictable manner. You can get four random
nibbles at a time from the above address which may be masked down to the
range of numbers you are interested in.
The program below takes two random numbers from the hardware crc, one
for the x coordinate and one for the y coordinate. Two dummy instructions
were planted inbetween the two readings. It turns out that without these
two dummy instructions, the static on the screen reduces to two predictable
lines.
Notice
======
I claim no proficiency at ML programming, but maybe this info will help others
get started. I did not hack out much of this. Most of it came from studying
the source code for SCHIP v1.0 by Erik Bryntse. For those not familiar with
this type of programming I suggest you actually run the program below and
recognize that only one pixel is being draw at a time...it's FAST!!! By the
way, press the drop key to EXIT.
The ML Freshie,
Zoom
catto@ecn.purdue.edu
P.S. Has anyone finished Ant? You must see the final round, it's a hoot.
The rolling (:() is actually quite easy, once you know how to get
by it. There's a lot more game after that.
--------------------------begin STAR source------------------------------------
;FUZZ: makes random static on screen, drop key exits
;r0 = data string pointer
;r1 = x coordinate in pixels
;r2 = key data storage
make_str = #05B7D
save_regs = #0679B
rest_regs = #067D2
crc_val = #00104
screen_ptr = #7055B
menu_ptr = #70551
in_c = #01160
header
code
call save_regs ;save d0, d1, b, d
clr.w c ;c = 0
move.w c, r2 ;initialize key storage
clr.w a ;a = 0
move.p3 #140, c ;c = data string size (64*5+4)
call make_str ;make a data string
swap.a a, d0 ;a = string address
move.w a, r0 ;save string address in r0
move.a a, d0 ;point to string
move.5 screen_ptr, d1 ;d1 = address of screen grob
move.p2 56, a ;a = # rows in screen grob
call fill_table ;make data table
move.5 menu_ptr, d1 ;d1 = address of menu grob
move.p2 8, a ;a = # rows in menu grob
call fill_table ;complete data table
clrb 15, st ;interupt flag off
intoff ;turn off interupts
main: move.5 crc_val, d0 ;d0 = address of hardware crc
move.a @d0, c ;c = pseudo random #
clr.a a ;a = 0
move.p2 #7F, a ;a = mask for screen width
and.a a, c ;c = random and x mask
move.w c, r1 ;save x coordinate
move.5 in_c, d0 ;these two lines do nothing
move.a @d0, c ;except make the crc random
;it's a numbers game
move.5 crc_val, d0 ;point to crc again
move.a @d0, c ;c = pseudo random #
move.p2 #3F, a ;a = mask for screen height
and.a a, c ;c = random and y mask
move.a c, a ;a = c = y coordinate
add.a c, c ;c = 2*c
add.a c, c ;c = 4*c
add.a a, c ;c = 5*c
move.w r0, a ;get data string address
add.a a, c ;add offset for y coordinate
move.a c, d0 ;point to row address
move.a @d0, c ;get row address
move.w r1, a ;get x coordinate in pixels
srb.w a ;a = x/2 (shift right bit)
srb.w a ;a = x/4 = x coordinate in nibs
add.a c, a ;a = row start + (x offset)
move.a a, d0 ;d0 points to nib of interest
move.w r1, c ;c = x coordinate in pixels
clr.a a ;a = 0
move.p1 3, a ;a = 3 (#11b)
and.a c, a ;get pixel position in nib
clr.a c ;c = 0
move.p1 1, c ;set up c
;mirror_nib routine makes a nibble containing one bit set a x coordinate.
;While grob nibbles are in order from left to right (up in memory),
;the bits within the nibble are in reverse order. So this routine
;puts the nibble through a u-turn.
mirror_nib: dec.a a ;a = a - 1
;if a(old) = 0 then sets carry
brcs ready ;jump if carry is set
add.a c, c ;c = 2*c = shift left bit c
jump mirror_nib ;loop
;Now the old nibble of the lcd grob is xor-ed with the new nibble
;BTW a nibble is just a four bit chunk, a hex digit (ex. #1111b = #Fh = #15d)
ready: clr.a a ;xor old and new nibble
move.1 @d0, a ;a = old nibble (OLD)
clr.w d
move.a c, d ;d = new nibble (NEW)
move.a a, c ;a = c = OLD
and.a d, c ;c = OLD and NEW
swap.a c, d ;d = OLD and NEW
or.a a, c ;c = OLD or NEW
not.a d ;d = not(OLD and NEW)
and.a d, c ;c = OLD xor NEW
move.1 c, @d0 ;place xor nibble
move.p3 #010, c ;#010 specifies the row of
out.x c ;the drop key, the out.x
;instruction scans a row
call in_c ;in_c returns the results
;you have to call this command
;something to do with return
;stack
move.a c, a ;a = results
clr.a c ;c = 0
out.x c ;stop scanning
;the following checks if the drop was pressed AND released for program exit
;that way your not pushing the drop key when program exits
move.w r2, c ;get old key status
move.w a, r2 ;put new key status
not.a a ;a = keys not pressed
and.a c, a ;and keys that were pressed
brbs 0, a, exit ;bit 0 of contains flag
jump main ;for drop key column
;fill_table: make a data table containing addresses of lcd rows
;IN: d1 points to grob data
; d0 points to data string
; a = # rows to set up
;AFFECTS: a, c, d0
fill_table: move.a @d1, c ;c = grob adress
add.a 16, c ;add 20 to skip grob header
add.a 4, c
ftlp: move.a c, @d0 ;put row address into table
add.a 16, c ;add 34 to skip to next row
add.a 16, c ;lcd grob width = 34 * 4 = 136
add.a 2, c
add.a 5, d0 ;skip to next address space
dec.b a ;decrement row counter
brnz.b a, ftlp ;continue if counter <> 0
ret
exit: inton ;interupts on (keyboard)
setb 15, st ;flag interupts on
call rest_regs ;recall d, b, d1, d0
move.a @d0, a ;return to RPL operating system
add.a 5, d0
jump @a
endcode
-------------------------------end STAR source---------------------------------